class Vue1{
  constructor(options)
  {
    this.$el = options.el
    this.$data = options.data
    this.$options = options
    if(this.$el){
      //实现一个数据的观察者observer
      new Observer(this.$data)
      //实现一个指令解析器complie
      new Complie(this.$el,this)
      //在methods里如果想修改数据this.msg = "mcgee0102"要设置一个代理
      this.proxyData(this.$data) //把this.$data 代理到this上
    }
  }

  proxyData(data){
    for(const key in data){
       Object.defineProperty(this,key,{
         get(){
           return data[key]
         },
         set(newVal){
           data[key] = newVal
         }
       })
    }
  }
}

const complieUtil = {
    getVal(expr,vm){
      //["msg","name"]  data为累积器，currentVal为当前值，参数2为累积器默认值
      return expr.split(".").reduce((data,currentVal)=>{
        return data[currentVal]
      },vm.$data)
    },
    setVal(expr,vm,inputVal){
      return expr.split(".").reduce((data,currentVal)=>{
        if(typeof data[currentVal]!=="object"){
          data[currentVal] = inputVal
        }
        return data[currentVal]
      },vm.$data)
     
    },
    getContentVal(expr,vm){
      return expr.replace(/\{\{(.+?)\}\}/g,(...args)=>{
        return this.getVal(args[1],vm) 
      })
    },
    //vm.$data[expr]  <div v-text="msg"></div>可以匹配，如果是msg.name那么前面那个注释获取不到$data
    //expr 还有可能{{}}
    text(node,expr,vm){ //node元素，expr元素内的属性值
      let value;
      if(expr.indexOf("{{")!==-1)
      {
        value = expr.replace(/\{\{(.+?)\}\}/g,(...args)=>{
          // console.log(args)
          new Watcher(vm,args[1],(newVal)=>{
            this.updater.textUpdater(node,this.getContentVal(expr,vm))
          })
          return this.getVal(args[1],vm) 
        })
      }else{
        new Watcher(vm,expr,(newVal)=>{
          this.updater.textUpdater(node,newVal)
        })
        value = this.getVal(expr,vm)
      }
      this.updater.textUpdater(node,value)
    },
    html(node,expr,vm){
      const value = this.getVal(expr,vm)
      new Watcher(vm,expr,(newVal)=>{              //绑定一个watcher，当我编译完后给他注册个数据劫持得watcher
        this.updater.htmlUpdater(node,newVal)     //数据变化通过回调更新新值，然后更新界面
      })
      this.updater.htmlUpdater(node,value)
    },
    model(node,expr,vm){
      const value = this.getVal(expr,vm)
      //数据驱动视图
      new Watcher(vm,expr,(newVal)=>{
        this.updater.modelUpdater(node,newVal)
      })
      //视图驱动数据
      node.addEventListener('input',(e)=>{
        this.setVal(expr,vm,e.target.value) 
      })

      this.updater.modelUpdater(node,value)
    },
    on(node,expr,vm,eventName){
      let fn = vm.$options.methods && vm.$options.methods[expr]
      node.addEventListener(eventName,fn.bind(vm),false) //不用bindfn绑定再node上，我要绑定到vm上
    },
    updater:{
      textUpdater(node,value){
        node.textContent = value
      },
      htmlUpdater(node,value){
        node.innerHTML = value
      },
      modelUpdater(node,value){
        node.value = value
      }
    }
}

class Complie{
  constructor(el,vm){
    this.el = this.isElementNode(el)?el:document.querySelector(el)//是否是元素节点对象
    // console.log(this.el)
    this.vm = vm;
    //获取文档碎片对象，（编译根节点下面的所有子节点，每次拿出子节点进行数据替换，都会引起页面的回流和重绘，影响性能）
    //文档碎片作用是他能把我们替换出的东西放在缓存中，用拿出来，不用不拿
    //替换成文档碎片对象，把他放入内存中,他会减少页面的回流和重绘
    const fragment = this.node2Fragment(this.el)
    // console.log(fragment)

    //编译fragment模板
    this.complie(fragment)

    //追加子元素到根元素上
    this.el.appendChild(fragment)
  }
  isElementNode(node){
    return node.nodeType === 1;
  }

  node2Fragment(el){
    //创建文档碎片
    const f = document.createDocumentFragment()
    let firstChild;
    while(firstChild = el.firstChild){
      f.appendChild(firstChild)
    }
    return f;
  }

  complie(fragment){
    //获取子节点
    const childNodes = fragment.childNodes;
    [...childNodes].forEach((childNode)=>{
      // console.log(childNode)  //空格换行是#text文本节点
      if(this.isElementNode(childNode)){
        //是元素节点
        // console.log("元素节点",childNode)
        this.complieElement(childNode)
      }else{
        //是文本节点
        // console.log("文本节点",childNode)
        this.complieText(childNode)
      }
      //递归，子节点还有孩子
      if(childNode.childNodes && childNode.childNodes.length)
      {
        this.complie(childNode)
      }
    })
  }

  complieElement(node){
    const attributes = node.attributes;//拿到元素节点的属性
    [...attributes].forEach((attr)=>{
      // console.log(attr)
      const {name,value} = attr
      if(this.isDirective(name)){ //是指令v-on:click ,v-text

        const [,dirctive] = name.split("-"); //text ,on:click
        const [dirname,eventName] = dirctive.split(":"); //[on,click]
        //根据不同的字符串处理不同的事件（complieUtil是个对象）
        //参数1节点，参数2节点属性的值，参数3vm（用来找到vm里的data好替换）,有事件把事件名传进去
        complieUtil[dirname](node,value,this.vm,eventName)
        //删除有v-指令标签的属性
        node.removeAttribute('v-'+dirctive)
      }else if(this.isEventName(name)){
        //使用@
        const [,eventName] = name.split("@")
        complieUtil["on"](node,value,this.vm,eventName)
      }
    })
  }
  complieText(node){
    //{{}}
    const content = node.textContent
    if(/\{\{(.+?)\}\}/.test(content)){
      complieUtil['text'](node,content,this.vm)
    }
  }

  isDirective(attrName){
    return attrName.startsWith('v-')
  }

  isEventName(attrName){
    return attrName.startsWith('@')
  }
}